package com.cragon.factory;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @Description: TODO
 * @Author: ZHOUHAILONG
 * @Date: 2020年11月02日 10:35
 */
public class InstanceFactory {
	public static Object getInstance(Object target) throws Exception {
		Class<?>[] interfaces = target.getClass().getInterfaces();

		if (interfaces.length == 0) {
			throw new RuntimeException("There is no interface!");
		}

		Class<?> inter = interfaces[0];
		String enter = "\n";
		String tab = "\t";
		StringBuffer packagePath = new StringBuffer("package com.cragon.proxy;" + enter + enter);

		String interName = inter.getSimpleName();
		String firstLine = "public class MyProxy implements " + interName + " {" + enter;
		String field = tab + "private " + interName + " target;" + enter + enter;

//		public MyProxy(XX target) {
//			this.target = target;
//		}
		String constructorFirstLine = tab + "public MyProxy(" + interName + " target) {" + enter;
		String constructorContent = tab + tab + "this.target = target;" + enter;
		String constructorLastLine = tab + "}" + enter + enter;
		packagePath.append("import " + inter.getName() + ";" + enter + enter).append(firstLine).append(field).append(constructorFirstLine).append(constructorContent).append(constructorLastLine);

		Method[] declaredMethods = inter.getDeclaredMethods();
		for (Method declaredMethod : declaredMethods) {
			Class<?> returnType = declaredMethod.getReturnType();
			String methodFirstLine = tab + "public " + returnType.getSimpleName() + " " + declaredMethod.getName() + "(";
			Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
			String parameterDefinition = "";
			String parameterContent = "";
			for (int i = 0; i < parameterTypes.length; i++) {
				parameterDefinition += parameterTypes[i].getSimpleName() + " p" + i + ",";
				parameterContent += "p" + i + ",";
			}

			if (parameterDefinition.length() > 0) {
				parameterDefinition = parameterDefinition.substring(0, parameterDefinition.length() - 1);
				parameterContent = parameterContent.substring(0, parameterContent.length() - 1);
			}

			methodFirstLine += parameterDefinition + ") {" + enter;
			methodFirstLine += tab + tab + "System.out.println(\"" + declaredMethod.getName() + ": \");" + enter;
			if (returnType.getName().equalsIgnoreCase("void")) {
				methodFirstLine += tab + tab + "this.target." + declaredMethod.getName() + "(" + parameterContent + ");" + enter + tab + "}" + enter;
			} else {
				methodFirstLine += tab + tab + "return this.target." + declaredMethod.getName() + "(" + parameterContent + ");" + enter + tab + "}" + enter;
			}
			packagePath.append(methodFirstLine);
		}

		String lastLine = "}";
		packagePath.append(lastLine);

		System.out.println(packagePath);

		File file = new File("D:\\com\\cragon\\proxy\\MyProxy.java");
		if (!file.exists()) {
			file.createNewFile();
		}
		FileOutputStream fos = new FileOutputStream(file);
		fos.write(packagePath.toString().getBytes());
		fos.flush();
		fos.close();

		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
		Iterable<? extends JavaFileObject> javaFileObjects = manager.getJavaFileObjects(file);
		JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, javaFileObjects);
		task.call();
		manager.close();

		URL[] url = new URL[]{new URL("file:D:\\\\")};
		URLClassLoader loader = new URLClassLoader(url);
		Class<?> aClass = loader.loadClass("com.cragon.proxy.MyProxy");
		Constructor<?> constructor = aClass.getConstructor(inter);

		return constructor.newInstance(target);
	}
}
